package com.yungu.swift.api.facade;


import com.alibaba.dubbo.config.annotation.Reference;
import com.google.common.collect.Lists;
import com.yungu.swift.assets.driver.model.dto.CarModelDto;
import com.yungu.swift.assets.driver.model.dto.CarModelValuationDto;
import com.yungu.swift.assets.driver.model.vo.ApiCarModelVo;
import com.yungu.swift.assets.driver.service.CarModelService;
import com.yungu.swift.assets.driver.service.CarModelValuationService;
import com.yungu.swift.assets.passenger.model.vo.PassengerVo;
import com.yungu.swift.assets.passenger.service.PassengerService;
import com.yungu.swift.autoconfig.redis.RedisCacheService;
import com.yungu.swift.base.ResponseData;
import com.yungu.swift.constants.MagicConstant;
import com.yungu.swift.constants.OrderConstant;
import com.yungu.swift.lbs.model.vo.FenceVo;
import com.yungu.swift.lbs.service.FenceService;
import com.yungu.swift.lbs.service.LocationService;
import com.yungu.swift.order.model.dto.OrderDetailDto;
import com.yungu.swift.order.model.dto.OrderDto;
import com.yungu.swift.order.model.dto.OrderFareDto;
import com.yungu.swift.order.model.param.ApiValuationParam;
import com.yungu.swift.order.model.param.JoinOrderParam;
import com.yungu.swift.order.model.vo.JsonOrderDetailVo;
import com.yungu.swift.order.model.vo.OrderVo;
import com.yungu.swift.order.service.*;
import com.yungu.swift.system.sys.model.dto.SysAssignConfigDto;
import com.yungu.swift.system.sys.model.dto.SysCityDto;
import com.yungu.swift.system.sys.model.dto.SysFenceDto;
import com.yungu.swift.system.sys.service.SysAssignConfigService;
import com.yungu.swift.system.sys.service.SysBusinessService;
import com.yungu.swift.system.sys.service.SysCityService;
import com.yungu.swift.system.sys.service.SysFenceService;
import com.yungu.swift.utils.DateUtils;
import com.yungu.swift.utils.MapUtils;
import com.yungu.swift.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.Instant;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

import static com.yungu.swift.constants.CommonConstant.*;
import static com.yungu.swift.order.model.enums.OrderErrorEnum.ERR_CODE_NO_PRICE_RULE;
import static java.util.regex.Pattern.compile;

/**
 * Created by cuixiuyin on 2018/4/11.
 */
@Slf4j
@Component
public class PassengerFacade {

    private ScheduledExecutorService SCHEDULE_POOL = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
            new BasicThreadFactory.Builder().namingPattern("create-order-schedule-assign-pool-%d").daemon(true).build());

    @Autowired
    private RedisCacheService redisCacheService;
    @Reference
    private PassengerService passengerService;
    @Reference
    private OrderService orderService;
    @Reference
    private OrderJoinService orderJoinService;
    @Reference
    private OrderRiskControlService orderRiskControlService;
    @Reference
    private OrderAssignService orderAssignService;
    @Reference
    private OrderFareService orderFareService;
    @Reference
    private SysCityService sysCityService;
    @Reference
    private CarModelService carModelService;
    @Reference
    private CarModelValuationService carModelValuationService;
    @Reference
    private SysAssignConfigService sysAssignConfigService;
    @Reference
    private SysBusinessService sysBusinessService;
    @Reference
    private SysFenceService sysFenceService;
    @Reference
    private FenceService fenceService;
    @Reference
    private LocationService locationService;

    /**
     * 估价接口
     *
     * @param valuationParam valuationParam 估价参数
     */
    public ResponseData<List<ApiCarModelVo>> valuation(ApiValuationParam valuationParam) {
        List<CarModelDto> carModelDtos = listCarModel(valuationParam.getCarModelUuid(),
                valuationParam.getAppid(), valuationParam.getBusinessType()).getData();
        if (CollectionUtils.isEmpty(carModelDtos)) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "无开通车型，请联系管理员开通");
        }
        return ResponseData.buildSuccessResponse(carModelDtos.stream().map(carModelDto -> {
            ApiCarModelVo apiCarModelVo = new ApiCarModelVo();
            BeanUtils.copyProperties(carModelDto, apiCarModelVo);
            valuationParam.setCarModelUuid(carModelDto.getUuid());
            apiCarModelVo.setValuation(orderFareService.commonValuation(valuationParam).getData());
            return apiCarModelVo;
        }).collect(Collectors.toList()));
    }

    /**
     * 获取车型接口
     *
     * @param appid        租户标识
     * @param businessType 业务类型
     * @return
     */
    public ResponseData<List<CarModelDto>> listCarModel(String uuid, String appid, int businessType) {
        if (StringUtils.isNotEmpty(uuid)) {
            return ResponseData.buildSuccessResponse(Lists.newArrayList(carModelService.get(uuid).getData()));
        }
        Map<String, Object> map = MapUtils.build(4);
        map.put("appid", appid);
        map.put("businessType", businessType);
        return carModelService.list(map);
    }

    /**
     * 获取日租半日租计价规则接口
     *
     * @param adCode       区域代码
     * @param carModelUuid 车型 uuid
     * @param appid        租户标识
     * @param businessType 业务类型
     * @return
     */
    public ResponseData<List<Map<String, Object>>> listTypeTime(String adCode, String carModelUuid, String appid, Integer businessType) {
        //单车型设置
        CarModelDto carModelDto;
        List<CarModelDto> carModelDtos = listCarModel(carModelUuid, appid, businessType).getData();
        if (CollectionUtils.isEmpty(carModelDtos)) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "获取不到车型信息");
        }
        carModelDto = carModelDtos.get(0);

        Map<String, Object> map = MapUtils.build();
        map.put("adCode", adCode);
        map.put("carModelUuid", carModelDto.getUuid());
        map.put("businessUuid", sysBusinessService.getBusinessUuidByType(carModelDto.getBusinessType()).getData());
        map.put("appid", appid);
        map.put("status", MagicConstant.INT_ONE);
        List<CarModelValuationDto> carModelValuationDtos = carModelValuationService.listTypeTime(map).getData();
        if (CollectionUtils.isEmpty(carModelValuationDtos)) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "该车型暂未开通，无法获取价格");
        }
        //组装数据
        List<Map<String, Object>> resultList = new ArrayList();
        for (CarModelValuationDto carModelValuationDto : carModelValuationDtos) {
            Map<String, Object> item = MapUtils.build(4);
            item.put("uuid", carModelValuationDto.getUuid());
            StringBuilder sBuilder = new StringBuilder();
            if (TYPE_TIME_DAILY_RENTAL == carModelValuationDto.getOrderTypeTime()) {
                sBuilder.append("日租").append(carModelValuationDto.getStartDuration() / 60).append("小时（含")
                        .append(carModelValuationDto.getStartTrip()).append("公里）");
                item.put("typeTrip", 2);
            }
            if (TYPE_TIME_HALF_DAILY_RENTAL == carModelValuationDto.getOrderTypeTime()) {
                sBuilder.append("半日租").append(carModelValuationDto.getStartDuration() / 60).append("小时（含").append(
                        carModelValuationDto.getStartTrip()).append("公里）");
                item.put("typeTrip", 3);
            }
            item.put("name", sBuilder.toString());
            resultList.add(item);
        }
        return ResponseData.buildSuccessResponse("查询成功", resultList);
    }

    /**
     * 日租半日租估价
     *
     * @param passengerUuid 用户 uuid
     * @param appid         租户标识
     * @param priceRuleUuid 计价规则 uuid
     * @return ResponseData
     */
    public ResponseData<Map<String, Object>> valuateRent(String passengerUuid, String appid, String priceRuleUuid) {
        return orderFareService.rentValuate(passengerUuid, appid, priceRuleUuid);
    }

    /**
     * 创建订单业务
     *
     * @param orderVo
     * @return
     */
    public ResponseData<JsonOrderDetailVo> createOrder(OrderVo orderVo) {
        // 1. 城市信息
        Map<String, Object> cityMap = new HashMap<>(4);
        cityMap.put("adCode", orderVo.getOriginAreaCode());
        cityMap.put("businessUuid", sysBusinessService.getBusinessUuidByType(orderVo.getTypeModule()).getData());
        String cityUuid = sysCityService.getCityUuidByMap(cityMap).getData();
        if (StringUtils.isBlank(cityUuid)) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "获取不到城市开通信息");
        }
        String cityName = "";
        SysCityDto sysCityDto = sysCityService.get(cityUuid).getData();
        if (null != sysCityDto) {
            cityName = sysCityDto.getCity();
        }
        // 2. 营运信息
        Map<String, Object> build = MapUtils.build(4);
        build.put("districtUuid", cityUuid);
        build.put("businessType", orderVo.getTypeModule());
        SysAssignConfigDto assignConfigDto = sysAssignConfigService.get(build).getData();
        if (null != assignConfigDto) {
            Date deparTime = TYPE_TIME_REAL_TIME == orderVo.getTypeTime() ? new Date() : orderVo.getDeparTime();
            Date parseStart = DateUtils.parse(assignConfigDto.getOperationStartTime(), "HH:mm:ss");
            Date parseEnd = DateUtils.parse(assignConfigDto.getOperationEndTime(), "HH:mm:ss");
            // 考虑跨天情况
            if (parseStart.after(parseEnd)) {
                parseEnd = Date.from(parseEnd.toInstant().atZone(ZoneId.systemDefault()).plusDays(1).toInstant());
            }
            if (deparTime.after(parseStart) && deparTime.before(parseEnd)) {
                return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "用车定位为：" + cityName +
                        "，运营时间为" + assignConfigDto.getOperationStartTime() + "-" + assignConfigDto.getOperationEndTime() +
                        "，现已暂停服务，尽情谅解；");
            }
        }
        // 3. 下单信息
        PassengerVo passengerVo = passengerService.selInfo(orderVo.getPassengerUuid()).getData();
        if (passengerVo == null) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "获取不到用户信息");
        }
        if (null != orderVo.getPassengerLatDb()) {
            orderVo.setPassengerLat(orderVo.getPassengerLatDb().toString());
        }
        if (null != orderVo.getPassengerLngDb()) {
            orderVo.setPassengerLng(orderVo.getPassengerLngDb().toString());
        }
        orderVo.setTypeInteractive(OrderConstant.NEW_ORDER_TYPE_INTERACTIVE_PUSH);
        //判断预约时间的正确性
        if (TYPE_TIME_APPIONT == orderVo.getTypeTime() && null != orderVo.getDeparTime()) {
            orderVo.setTypeInteractive(OrderConstant.NEW_ORDER_TYPE_INTERACTIVE_SNATCH);
            if (orderVo.getDeparTime().getTime() <= System.currentTimeMillis()) {
                return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "预约时间不能小于当前时间");
            }
            //当订单为接送机业务时 若出发时间小于等于半个小时 则自动变成实时单 并且优先派单
            if ((NEW_ORDER_TYPE_TRIP_MEET == orderVo.getTypeTrip() || NEW_ORDER_TYPE_TRIP_SEND == orderVo.getTypeTrip())
                    && (orderVo.getDeparTime().getTime() - System.currentTimeMillis()) / 1000 / 60 <= 30) {
                orderVo.setTypeInteractive(OrderConstant.NEW_ORDER_TYPE_INTERACTIVE_PUSH);
            }
        }
        if (BUSINESS_TYPE_TAXI == orderVo.getTypeModule()) {
            orderVo.setTypeInteractive(OrderConstant.NEW_ORDER_TYPE_INTERACTIVE_SNATCH);
        }
        //判断能否下单
        String mobile = Optional.ofNullable(orderVo.getPassengerMobile()).orElse(orderVo.getPassengerMobile());
        ResponseData<Boolean> canPasCreateOrder = orderRiskControlService.canPasCreateOrder(orderVo.getPassengerUuid(),
                orderVo.getAppid(), null, mobile, orderVo.getTypeTime());
        if (!canPasCreateOrder.isSuccess() || canPasCreateOrder.getData() == null) {
            return ResponseData.buildErrorResponse(canPasCreateOrder.getErrCode(), canPasCreateOrder.getMsg());
        }
        // 4. 订单信息
        //保存订单主表order
        OrderDto orderDto = new OrderDto();
        BeanUtils.copyProperties(orderVo, orderDto);
        //订单类型（主/子订单）：1 主订单；2 子订单
        orderDto.setTypeMain(OrderConstant.ORDER_TYPE_MAIN);
        orderDto.setMainStatus(OrderConstant.ORDER_MAIN_STATUS_INITIAL);
        orderDto.setSubStatus(OrderConstant.ORDER_STATUS_WAIT);
        //保存订单详情表order_detail
        OrderDetailDto orderDetailDto = new OrderDetailDto();
        BeanUtils.copyProperties(orderVo, orderDetailDto);
        orderDetailDto.setRemark(orderVo.getRemark());
        //仅保存接口传来的数据，若未传则为空
        if (orderVo.getPassengerName() != null) {
            orderDetailDto.setActualPasNam(handPasName(orderVo.getPassengerName()));
        }
        //电话号永远有值
        orderDetailDto.setActualPasMob(mobile);
        //预约单唯一不同的字段
        orderDetailDto.setDeparTime(TYPE_TIME_REAL_TIME == orderVo.getTypeTime() ? new Date() : orderVo.getDeparTime());
        orderDetailDto.setOriginCityUuid(cityUuid);
        orderDetailDto.setDestCityUuid(sysCityService.getCityUuidByName(orderVo.getDestCity()).getData());

        // 5. 订单费用信息
        //保存订单价格表
        OrderFareDto orderFareDto = new OrderFareDto();
        orderFareDto.setAppid(orderDto.getAppid());
        //额外小费,不是附加服务费
        orderFareDto.setTip(orderVo.getServiceFare());
        //出租车不计算价格
        if (BUSINESS_TYPE_TAXI != orderVo.getTypeModule()) {
            ApiValuationParam valuationParam = new ApiValuationParam();
            valuationParam.setAdCode(orderVo.getOriginAreaCode());
            valuationParam.setBusinessType(orderVo.getTypeModule());
            valuationParam.setDepartTime(TYPE_TIME_REAL_TIME == orderVo.getTypeTime() ? new Date() : orderVo.getDeparTime());
            valuationParam.setPlanTime(orderVo.getPlanDuration());
            valuationParam.setPlanTrip(orderVo.getPlanTrip());
            valuationParam.setAppid(orderVo.getAppid());
            valuationParam.setCarModelUuid(orderVo.getCarModelUuid());
            ResponseData<List<ApiCarModelVo>> valuation = valuation(valuationParam);
            if (!valuation.isSuccess() || CollectionUtils.isEmpty(valuation.getData())) {
                return ResponseData.buildErrorResponse(valuation.getErrCode(), valuation.getMsg());
            }
            Map<String, Object> valuationMap = valuation.getData().get(0).getValuation();
            orderFareDto.setCarModelValuationUuid((String) valuationMap.get("carModelValuationUuid"));
            if (valuationMap.containsKey("totalFare")) {
                orderDetailDto.setPlanFare((Double) valuationMap.get("totalFare"));
            }
            if (valuationMap.containsKey("couponMoney")) {
                orderDetailDto.setPlanCouponFare(new BigDecimal((Double) valuationMap.get("couponMoney")));
            }
            if (valuationMap.containsKey("actPaid")) {
                orderDetailDto.setPlanTripFare(new BigDecimal((Double) valuationMap.get("actPaid")));
            }
        }else{
            // 计费方式
            Map<String, Object> params = MapUtils.build(8);
            params.put("appid", orderVo.getAppid());
            params.put("carModelUuid", orderVo.getCarModelUuid());
            params.put("areaID", orderVo.getOriginAreaCode());
            // 出发时间
            Date departTime = TYPE_TIME_REAL_TIME == orderVo.getTypeTime() ? new Date() : orderVo.getDeparTime();
            if (departTime == null) {
                departTime = new Date();
            }
            // 实时/预约
            int typeTime = 1;
            if (departTime.after(Date.from(Instant.now().plusMillis(30)))) {
                typeTime = 2;
            }
            params.put("orderTypeTime", typeTime);
            // 业务类型
            String businessUuid = sysBusinessService.getBusinessUuidByType(orderVo.getTypeModule()).getData();
            params.put("businessUuid", businessUuid);
            params.put("businessType",orderVo.getTypeModule());

            log.info("计费方式参数:{}" , params);

            CarModelValuationDto carValuationDto = carModelValuationService.getCarModel(params).getData();
            if (carValuationDto == null) {
                return ResponseData.buildErrorResponse(ERR_CODE_NO_PRICE_RULE.getCode(), ERR_CODE_NO_PRICE_RULE.getMsg());
            }
            orderFareDto.setCarModelValuationUuid(carValuationDto.getUuid());
        }
        //保存预估位置信息
        orderDetailDto.setPlanRoute(getEstimateRoute(orderDetailDto));
        //保存订单
        ResponseData<String> saveOrderInfo = orderService.saveOrderInfo(orderDto, orderDetailDto, orderFareDto);
        if (!saveOrderInfo.isSuccess()) {
            return ResponseData.buildErrorResponse(saveOrderInfo.getErrCode(), saveOrderInfo.getMsg());
        }
        //存入缓存，给重新叫车使用
        orderVo.setOrderUuid(saveOrderInfo.getData());

        redisCacheService.set(saveOrderInfo.getData(), orderVo, 10L);

        //获取详情
        ResponseData<JsonOrderDetailVo> orderDetailData = orderService.getOrderDetail(saveOrderInfo.getData(), null);
        log.info("--------订单创建完成--------");
        //执行推单或派单逻辑
        SCHEDULE_POOL.schedule(()->{
            log.info("--------执行派单动作--------");
            orderAssignService.assignRealTime(saveOrderInfo.getData());
        },1L, TimeUnit.SECONDS);
        return orderDetailData;
    }

    private String handPasName(String passengerName) {
        Matcher matcher = compile("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]").matcher(passengerName);
        if (matcher.find()) {
            try {
                passengerName = matcher.replaceAll(URLEncoder.encode(matcher.group(0), "UTF-8"));
            } catch (Exception e) {
                log.error("handPasName error.passengerName:{}", passengerName, e);
            }
        }
        return passengerName;
    }

    /**
     * 围栏设备监控
     *
     * @param lng 乘客经度
     * @param lat 乘客纬度
     * @param diu 设备标识
     * @return
     */
    public boolean statusFence(double lng, double lat, String diu, String appid) {
        Map<String, Object> map = sysFenceService.statusFence(lng, lat, diu).getData();
        Object status = map.get("status");
        return Objects.equals(status, "in");
    }


    /**
     * 查询是否进入围栏
     *
     * @param lng      乘客经度
     * @param lat      乘客纬度
     * @param diu      设备标识
     * @param areaCode 区域编码
     * @return
     */
    public boolean checkFence(double lng, double lat, String diu, String areaCode, String appid) {
        //判断是否进入高德围栏
        List<FenceVo> fenceList = fenceService.statusFence(lng, lat, diu).getData();
        if (CollectionUtils.isEmpty(fenceList)) {
            return false;
        }
        List<String> gidList = new ArrayList<>();
        for (FenceVo fenceVo : fenceList) {
            if ("in".equals(fenceVo.getStatus())) {
                gidList.add(fenceVo.getFenceGid());
            }
        }
        if (gidList.size() == 0) {
            return false;
        }
        //取出gid查询是否在数据库中
        Map<String, Object> paraMap = new HashMap<>();
        paraMap.put("fenceGidList", gidList);
        paraMap.put("areaCode", areaCode);
        List<SysFenceDto> resultList = sysFenceService.list(paraMap).getData();
        return CollectionUtils.isNotEmpty(resultList);
    }


    public ResponseData<Map<String, Object>> createJoinOrder(JoinOrderParam orderParam) {
        orderParam.setPassengerUuid(orderParam.getUserUuid());
        ResponseData<Map<String, Object>> createJoinOrder = orderJoinService.createCrosstownOrder(orderParam);
        if (createJoinOrder.isSuccess() && createJoinOrder.getData() != null) {
            redisCacheService.set(createJoinOrder.getData().get("orderUuid").toString(), orderParam, 10L);
        }
        return createJoinOrder;
    }

    /**
     * 获取预估路线
     */
    private String getEstimateRoute(OrderDetailDto orderDetailDto) {
        ResponseData<String> resultRsd = locationService.getEstimateRoute(orderDetailDto.getOriginLng(), orderDetailDto.getOriginLat(), orderDetailDto.getDestLng(), orderDetailDto.getDestLat());
        if (resultRsd.isSuccess()) {
            return resultRsd.getData();
        }
        return "";
    }
}
